import io
import base64
import json
import logging
import datetime
import random

from django.db.models import Q
from django.conf import settings
from django.utils import timezone
from django.http import JsonResponse, HttpResponse
from rest_framework.views import APIView
from rest_framework_jwt.settings import api_settings

from role.models import SysRole, SysUserRole
from user.models import SysUser, SysUserSerializer, SysLogs, TwoAuthStatus
from menu.models import SysMenu, SysRoleMenu, SysMenuSerializer
from utils.check_permission import check_permission

from utils.draw_code import check_code
from utils.encrypt import Password
from utils.page_data import PageData
from utils.send_msg_util.send_email_utils import send_email
from utils.single_user_login import SingleLogin

logger = logging.getLogger()
# 修改权限
logger.setLevel(logging.INFO)


class TwoAuthStatusView(APIView):
    """是否启用邮箱二次验证"""

    @check_permission(perms_list=['system:user:list'])  # 直接应用装饰器，无需method_decorator
    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        try:
            TwoAuthStatus(id=1, status=int(data["status"])).save()
        except Exception as e:
            logger.error(f"开启二次认证失败：{e}")
            return JsonResponse({"code": 500, "msg": str(e)})
        return JsonResponse({"code": 200})

    def get(self, request):
        return JsonResponse({"code": 200, "msg": TwoAuthStatus.objects.get().status})


class LoginView(APIView):
    """登陆"""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.__encryption_pass = Password()

    def get_user_agent(self, request):
        user_agent = request.META.get("HTTP_USER_AGENT")
        return user_agent

    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip

    # 构造菜单树
    def build_tree_menu(self, sys_menu_list):
        result_menu_list: list[SysMenu] = list()
        for menu in sys_menu_list:
            # 寻找子节点
            for item in sys_menu_list:
                if item.parent_id == menu.id:
                    if not hasattr(menu, "children"):
                        menu.children = list()
                    menu.children.append(item)
            # 判断父节点，添加到集合
            if menu.parent_id == 0:
                result_menu_list.append(menu)
        return result_menu_list

    def save_logs(self, request, status, remark, username):
        ip = self.get_client_ip(request)
        user_agent = self.get_user_agent(request)
        SysLogs.objects.create(username=username, status=status, login_ip=ip, user_agent=user_agent, remark=remark)

    def get(self, request):
        try:
            username = request.session.get('username')
            user = SysUser.objects.get(username=username)

            # 获取用户角色
            role_ids = SysUserRole.objects.filter(user_id=user.id).values_list('role_id', flat=True)
            roles_list = SysRole.objects.filter(id__in=role_ids).values('id', 'name')

            # 当前用户拥有的角色，逗号隔开
            roles = ','.join([role['name'] for role in roles_list])
            menu_set: set[SysMenu] = set()
            for row in roles_list:
                menu_ids = SysRoleMenu.objects.filter(role_id=row['id']).values_list('menu_id')
                menu_list = SysMenu.objects.filter(id__in=menu_ids).all()
                for row2 in menu_list:
                    menu_set.add(row2)
            menu_list: list[SysMenu] = list(menu_set)  # set转list
            sorted_menu_list = sorted(menu_list)  # 根据menu/models.py的order_num排序
            logging.info(roles)
            # 构造菜单树
            sys_menu_list: list[SysMenu] = self.build_tree_menu(sorted_menu_list)
            serializer_menu_list = list()
            for item in sys_menu_list:
                serializer_menu_list.append(SysMenuSerializer(item).data)
        except Exception as e:
            logging.error(e)
            return JsonResponse({'code': 500, 'msg': '获取menuList失败'})
        return JsonResponse({'code': 200, 'msg': serializer_menu_list})

    def post(self, request):
        try:
            data = json.loads(request.body.decode("utf-8"))
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': 401, 'msg': '用户名或密码错误'})
        username = data['username']
        password = data['password']
        password = self.__encryption_pass.encrypt(password)
        two_auth_status = TwoAuthStatus.objects.get().status
        if two_auth_status == 0:
            try:
                code = data['code']
            except Exception as e:
                logging.error(e)
                return JsonResponse({'code': 401, 'msg': '请输入验证码'})

            session_code = request.session.get('image_code', '')
            if code.upper() != session_code.upper():
                self.save_logs(request, 0, "验证码错误", username)
                return JsonResponse({'code': 401, 'msg': '验证码错误'})

        if two_auth_status == 1:
            try:
                cache_email_code = request.session.get('email_code')
                if not cache_email_code:
                    return JsonResponse({'code': 401, 'msg': '邮箱验证码错误'})
                email_code = data.get('emailCode')
                if int(cache_email_code) != int(email_code):
                    self.save_logs(request, 0, "邮箱验证码错误", username)
                    return JsonResponse({'code': 401, 'msg': '邮箱验证码错误'})
            except Exception as e:
                logger.error(f"邮箱验证码错误：{str(e)}")
                return JsonResponse({'code': 401, 'msg': f"邮箱验证码错误：{str(e)}"})

        request.session["email_code"] = ''

        # ================单用户登录逻辑=====================
        single_login = SingleLogin(username)
        single_login.set_black_list()
        # ================单用户登录逻辑=====================
        try:
            user = SysUser.objects.get(username=username, password=password)
            if not user:
                return JsonResponse({'code': 401, 'msg': '用户名或密码错误'})
            if user.status == 0:
                return JsonResponse({'code': 401, 'msg': '该用户已禁用'})

            # 修改token过期时间
            api_settings.JWT_EXPIRATION_DELTA = datetime.timedelta(seconds=30000)

            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            # 将用户对象传递进去，获取该对象的属性值
            payload = jwt_payload_handler(user)
            # 将属性值编码成jwt格式的字符串
            token = jwt_encode_handler(payload)

            # ================单用户登录逻辑=====================
            single_login.set_redis_user_token(token)
            # ================单用户登录逻辑=====================
        except Exception as e:
            logging.error(e)
            self.save_logs(request, 0, "用户名或密码错误", username)
            return JsonResponse({'code': 401, 'msg': '用户名或密码错误'})

        # 获取用户角色
        role_ids = SysUserRole.objects.filter(user_id=user.id).values_list('role_id', flat=True)
        roles_list = SysRole.objects.filter(id__in=role_ids).values('id', 'name')

        # 当前用户拥有的角色，逗号隔开
        roles = ','.join([role['name'] for role in roles_list])

        user_obj = SysUser.objects.filter(username=username)
        user_obj.update(login_date=timezone.now())
        self.save_logs(request, 1, "", username)

        # 把token保存到session中
        request.session['token'] = token

        # 把用户名保存到session中
        request.session["username"] = username
        request.session["user_id"] = user_obj.get().id
        role_list = []
        for item in user_obj.values_list('sysuserrole__role_id'):
            role_list.append(item[0])

        request.session["role_list"] = role_list

        # 给session设置30000s超时时间
        request.session.set_expiry(30000)

        return JsonResponse({'code': 200,
                             'token': token,
                             'user': SysUserSerializer(user).data,
                             'msg': '登陆成功',
                             'roles': roles,
                             })


class CodeView(APIView):
    """生成图片验证码"""

    def get(self, request):
        # 调用pillow函数，生成图片
        img, code_string = check_code()
        logging.warning(f"验证码：{code_string}")
        # 写入到自己的session中，以便后续获取验证码再进行校验
        request.session["image_code"] = code_string
        # 给session设置60s超时时间
        request.session.set_expiry(60)

        buffered = io.BytesIO()
        img.save(buffered, format="PNG")
        img_str = base64.b64encode(buffered.getvalue()).decode('utf-8')

        return HttpResponse(img_str)


class EmailCodeView(APIView):
    """获取邮箱验证码"""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.__encryption_pass = Password()

    def post(self, request):
        cache_email_code = request.session.get('email_code')
        if cache_email_code:
            return JsonResponse({'code': 401, 'msg': '请勿重复获取验证码'})

        data = json.loads(request.body.decode("utf-8"))
        username = data['username']
        try:
            user = SysUser.objects.get(username=username)

            # 生成随机6位数字（000000-999999）
            random_number = random.randint(0, 999999)
            code_string = f"{random_number:06d}"  # 确保不足6位时前面补0
            logging.warning(f"邮箱验证码：{code_string}")
            # 写入到自己的session中，以便后续获取验证码再进行校验
            request.session["email_code"] = code_string
            # 给session设置60s超时时间
            request.session.set_expiry(60)

            send_email("邮箱验证码", user.email, msg=code_string)
        except Exception as e:
            logger.error(f"获取邮箱验证码失败：{e}")
            return JsonResponse({'code': 500, 'msg': '获取邮箱验证码失败，请重试'})
        return JsonResponse({"code": 200})


class UpdateUserStatusView(APIView):
    """修改用户禁用状态"""

    def post(self, request):
        try:
            data = json.loads(request.body.decode("utf-8"))
            user_id = data['id']
            status_id = data['status']
            SysUser.objects.filter(id=user_id).update(status=status_id)
        except Exception as e:
            logging.error(e)
            return JsonResponse({'code': 500, 'msg': '执行失败'})
        return JsonResponse({'code': 200})


class SaveView(APIView):
    """保存用户信息"""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.__encryption_pass = Password()

    def post(self, request):
        try:
            data = json.loads(request.body.decode("utf-8"))
            if data["id"] == -1:  # 添加
                SysUser.objects.create(username=data['username'],
                                       password=self.__encryption_pass.encrypt(data['password']),
                                       avatar='default.jpg',
                                       email=data['email'],
                                       phone_number=data['phone_number'],
                                       status=data['status'],
                                       remark=data['remark'])
            else:  # 修改
                if data['username'] == 'admin':
                    return JsonResponse({"code": 500, "msg": "修改失败"})
                obj_sys_user = SysUser(id=data['id'],
                                       username=data['username'],
                                       password=self.__encryption_pass.encrypt(data['password']),
                                       avatar=data['avatar'],
                                       email=data['email'],
                                       phone_number=data['phone_number'],
                                       status=data['status'],
                                       login_date=data['login_date'],
                                       create_time=data['create_time'],
                                       remark=data['remark'])
                obj_sys_user.save()
        except Exception as e:
            logging.error(e)
            return JsonResponse({'code': 500, 'msg': '执行失败'})
        return JsonResponse({'code': 200})


class PwdView(APIView):
    """修改密码"""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.__encryption_pass = Password()

    def post(self, request):
        try:
            data = json.loads(request.body.decode("utf-8"))
            user_id = data['id']
            username = data['username']
            old_pwd = self.__encryption_pass.encrypt(data['oldPassword'])
            new_pwd = self.__encryption_pass.encrypt(data['newPassword'])
            obj = SysUser.objects.filter(id=user_id, username=username, password=old_pwd)
            if obj:
                obj.update(password=new_pwd)
            else:
                return JsonResponse({"code": 500, "msg": "旧密码输入错误"})
        except Exception as e:
            logging.error(e)
            return JsonResponse({"code": 500, "msg": "密码修改失败"})
        return JsonResponse({"code": 200})


class ImageView(APIView):
    """上传头像"""

    def post(self, request):
        file = request.FILES.get('avatar')
        logging.info(f"file: {file}")
        if file:
            file_name = file.name
            suffix_name = file_name[file_name.rfind("."):]
            new_file_name = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + suffix_name
            file_path = str(settings.MEDIA_ROOT) + "/userAvatar/" + new_file_name
            logging.info(f"file_path: {file_path}")
            try:
                with open(file_path, "wb") as f:
                    for chunk in file.chunks():
                        f.write(chunk)
                return JsonResponse({"code": 200, "title": new_file_name})
            except Exception as e:
                logging.error(e)
                return JsonResponse({"code": 500, "msg": "上传头像失败"})


class AvatarView(APIView):
    """修改头像"""

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        id = data["id"]
        avatar = data["avatar"]
        obj_user = SysUser.objects.get(id=id)
        obj_user.avatar = avatar
        obj_user.save()
        return JsonResponse({"code": 200})


class SearchView(APIView):
    """用户信息查询"""

    @check_permission(perms_list=['system:user:list'])  # 直接应用装饰器，无需method_decorator
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        query = data["query"]
        page_num = data["pageNum"]  # 当前页
        page_size = data["pageSize"]  # 每页大小
        try:
            user_queryset = (SysUser.objects.filter(username__icontains=query).order_by('id')
                             .values('id', 'username', 'avatar', 'email', 'phone_number',
                                     'login_date', 'status', 'create_time', 'update_time', 'remark'))

            # 使用PageData 类，获取分页数据
            user_list_obj = PageData(user_queryset, page_size, page_num)
            user_list = user_list_obj.get_search()

            for user in user_list:
                try:
                    user['login_date'] = user['login_date'].strftime('%Y-%m-%d %H:%M:%S')
                    user['create_time'] = user['create_time'].strftime('%Y-%m-%d %H:%M:%S')
                except AttributeError as e:
                    logger.error(str(e))
                # 获取用户角色
                role_ids = SysUserRole.objects.filter(user_id=user['id']).values_list('role_id')
                roles_list = SysRole.objects.filter(id__in=role_ids).values('id', 'name')
                # 将用户信息和角色字符串一起添加到结果列表中
                role_list_dict = []
                for role in roles_list:
                    role_dict = dict()
                    role_dict['id'] = role['id']
                    role_dict['name'] = role['name']
                    role_list_dict.append(role_dict)
                user['roleList'] = role_list_dict
            count = user_queryset.count()
            return JsonResponse({
                "code": 200,
                "count": count,
                "msg": user_list,
            })
        except Exception as e:
            logger.error(str(e))
            return JsonResponse({"code": 500, "msg": str(e)})


class ResetPwdView(APIView):
    """重置密码"""

    @check_permission(perms_list=['system:user:list'])  # 直接应用装饰器，无需method_decorator
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.__encryption_pass = Password()

    def post(self, request):
        try:
            # id通过body传到后端，不需要json.loads,因为整个body只有这个id
            user_id = int(request.body.decode("utf-8"))
            if user_id == 1:
                return JsonResponse({"code": 500, "msg": "重置失败"})
            res = SysUser.objects.filter(id=user_id)
            if res:
                res.update(password=self.__encryption_pass.encrypt('123456'))
            else:
                return JsonResponse({"code": 500, "msg": "用户不存在"})
        except Exception as e:
            logging.error(e)
            return JsonResponse({"code": 500, "msg": "重置失败，请联系管理员"})
        return JsonResponse({"code": 200})


class DelUserView(APIView):
    """删除用户"""

    @check_permission(perms_list=['system:user:list'])  # 直接应用装饰器，无需method_decorator
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def delete(self, request):
        try:
            user_id_list = json.loads(request.body.decode("utf-8"))
            for item in user_id_list:
                if item == 1:
                    return JsonResponse({"code": 500, "msg": "删除失败"})
            SysUserRole.objects.filter(user_id__in=user_id_list).delete()
            SysUser.objects.filter(id__in=user_id_list).delete()
        except Exception as e:
            logging.error(e)
            return JsonResponse({"code": 500, "msg": "用户删除失败，请联系管理员"})
        return JsonResponse({"code": 200})


class CheckUserView(APIView):
    """查询用户是否存在"""

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        obj = SysUser.objects.filter(username=data['username'])
        if obj:
            logging.warning('用户已存在')
            return JsonResponse({"code": 500})
        return JsonResponse({'code': 200})


class ActionCheckUserView(APIView):
    """获取新建用户的数据"""

    @check_permission(perms_list=['system:user:list'])  # 直接应用装饰器，无需method_decorator
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        user_id = request.GET.get('id')
        obj = SysUser.objects.get(id=user_id)
        if obj:
            return JsonResponse({"code": 200, "msg": SysUserSerializer(obj).data})
        return JsonResponse({"code": 500, 'msg': '用户不存在'})


class GrantRoleView(APIView):
    """用户角色管理"""

    @check_permission(perms_list=['system:user:list'])  # 直接应用装饰器，无需method_decorator
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        role_list = data['roleList']
        user_id = data['id']
        if user_id == 1:
            return JsonResponse({"code": 500, "msg": "角色分配失败"})
        try:
            SysUserRole.objects.filter(user_id=user_id).delete()
            for item in role_list:
                SysUserRole.objects.create(user_id=user_id, role_id=item)
        except Exception as e:
            logging.error(e)
            return JsonResponse({"code": 500, "msg": "角色分配失败"})
        return JsonResponse({"code": 200})


class SysLogsView(APIView):
    """获取登陆日志"""

    @check_permission(perms_list=['system:logs:list'])  # 直接应用装饰器，无需method_decorator
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        query = data["query"]
        page_num = data["pageNum"]  # 当前页
        page_size = data["pageSize"]  # 每页大小
        status_id = data["statusID"]
        if status_id is None:
            status_id = 2
        try:
            obj = SysLogs.objects
            if int(status_id) == 2:
                sys_logs_queryset = (obj.filter(Q(username__icontains=query) | Q(login_ip=query))
                                     .order_by('-login_time').values())
            elif int(status_id) == 1:
                sys_logs_queryset = (obj.filter(Q(status=1) & Q(username__icontains=query) | Q(login_ip=query))
                                     .order_by('-login_time').values())
            else:
                sys_logs_queryset = (obj.filter(Q(status=0) & Q(username__icontains=query) | Q(login_ip=query))
                                     .order_by('-login_time').values())

            # 使用PageData 类，获取分页数据
            sys_logs_obj = PageData(sys_logs_queryset, page_size, page_num)
            sys_logs_list = sys_logs_obj.get_search()
            for item in sys_logs_list:
                try:
                    item['login_time'] = item['login_time'].strftime('%Y-%m-%d %H:%M:%S')
                except AttributeError as e:
                    logger.error(str(e))
        except Exception as e:
            logging.error(e)
            return JsonResponse({"code": 500, "msg": "获取失败"})

        total = sys_logs_queryset.count()
        return JsonResponse({
            "code": 200,
            "total": total,
            "msg": sys_logs_list,
        })
